/*
 * Copyright 2010
 * Jairou Software Organization
 * All Rights Reserved.
 * 
 * This software is the proprietary 
 * information of Roger Ramia
 * and the Jairou Software Organization 
 * Use is subject to license terms.
 * 
 * For licensing information please visit
 * http://www.jairou.org/license
 * 
 * @author Roger Ramia
 */
package org.jairou.util;

import java.io.FileReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.jairou.core.Objects;
import org.jairou.core.XML;


/**
 * Common utility providing core server side
 * Javascript support and uniform access to 
 * the embeded Rhino ScriptEngineManager of JDK 1.6
 * <br/>
 * This class enables the loading and invoking
 * of java script files as defined in by the jairou.xml
 * file.
 * <br/>
 * For example: 
 *  <pre>{@code
 * 	<js-engine>
 *		<bindings>
 *			<import id="XML" class="org.jairou.labs.core.XML"/>
 *			<import id="DB" class="org.jairou.labs.util.DB"/>
 *			<import id="Security" class="org.jairou.labs.util.Security"/>
 *			<import id="Email" class="org.jairou.labs.util.Email"/>
 *		</bindings>
 *		<scripts>
 *			<file id="validationrules.js"/>
 * 			<file id="businesslogic.js"/>
 *		</scripts>
 *	</js-engine>
 * }</pre>
 * The <bindings> section defines objects you would like to import into 
 * the script engine and make available to all javascripts functions.
 * Any class specified in the bindings will be instanciated once using
 * it's default constructor and injected into the ScriptEngine's global
 * bindings.  The Object will be keyed by it's respective id attribute.
 * <br/>
 * The <scripts> section defines all the .js files you'd like to load
 * into the script engine.  All functions in all files are accessible
 * via the JS.invoke() and JS.checkRule() methods.
 * <pre>
 * 	JS.invoke() - calls a js function and returns the resulting object
 *  JS.checkRule() - calls a js function and returns a boolean
 * </pre>
 *  
 */
public class JS {
	
	private static ScriptEngineManager manager; 
	private static ScriptEngine engine;
	private static String root;
	
	public static final String CONTEXT_KEY = "ctx";
	
	static {
		try {
			manager = new ScriptEngineManager();
			engine = manager.getEngineByName("js");
			root = XML.get(XML.ROOT_DIRECTORY_KEY);
			
			// import bindings
			for(String key :  XML.getIds("js-engine.bindings.import")) {
				String clsName = XML.get("js-engine.bindings.import."+key+".class");
				manager.put(key, Objects.newInstance(clsName));
			}
			
			// load scripts
			for (String script : XML.getIds("js-engine.scripts.file")) {
				if (script != null) {
					FileReader reader = new FileReader(root+"/"+script);
					engine.eval(reader);
				}
			}		
		}
		catch(Exception e) {
			System.out.println("failed to initialize JS engine");
			e.printStackTrace();
		}
	}
	
	/**
	 * @param id
	 * @throws Exception
	 */
	public static Object invoke(String id, Object[] params, Object ctx) 
	throws Exception {
		Object out = null;
		if (engine != null) {
			if (ctx != null) engine.put(CONTEXT_KEY, ctx);
			if (params != null)	out = ((Invocable) engine).invokeFunction(id, params);
			else out = ((Invocable) engine).invokeFunction(id);
		}
		return out;
	}
	
	public static Object invoke(String id, Object ctx) 
	throws Exception {
		return invoke(id, null, ctx);
	}	
	
	public static Object invoke(String id) 
	throws Exception {
		return invoke(id, null, null);
	}	
	
	/**
	 * @param id
	 * @throws Exception
	 */
	public static boolean checkRule(String id, String value, Object ctx) 
	throws Exception {
		Object out = null;
		out = invoke(id, new Object[]{value}, ctx);
		if (out != null) return (Boolean)out;
		else return false;
	}
}